// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/child/web_url_loader_impl.h"

#include <stdint.h>

#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/optional.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/mime_util/mime_util.h"
#include "content/child/child_thread_impl.h"
#include "content/child/ftp_directory_listing_response_delegate.h"
#include "content/child/request_extra_data.h"
#include "content/child/resource_dispatcher.h"
#include "content/child/shared_memory_data_consumer_handle.h"
#include "content/child/sync_load_response.h"
#include "content/child/web_url_request_util.h"
#include "content/child/weburlresponse_extradata_impl.h"
#include "content/common/resource_messages.h"
#include "content/common/resource_request.h"
#include "content/common/resource_request_body_impl.h"
#include "content/common/service_worker/service_worker_types.h"
#include "content/common/url_loader.mojom.h"
#include "content/public/child/fixed_received_data.h"
#include "content/public/child/request_peer.h"
#include "content/public/common/browser_side_navigation_policy.h"
#include "mojo/public/cpp/bindings/associated_group.h"
#include "net/base/data_url.h"
#include "net/base/filename_util.h"
#include "net/base/net_errors.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/ct_sct_to_string.h"
#include "net/cert/x509_util.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_util.h"
#include "net/ssl/ssl_cipher_suite_names.h"
#include "net/ssl/ssl_connection_status_flags.h"
#include "net/url_request/url_request_data_job.h"
#include "third_party/WebKit/public/platform/FilePathConversion.h"
#include "third_party/WebKit/public/platform/WebHTTPLoadInfo.h"
#include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
#include "third_party/WebKit/public/platform/WebSecurityStyle.h"
#include "third_party/WebKit/public/platform/WebURL.h"
#include "third_party/WebKit/public/platform/WebURLError.h"
#include "third_party/WebKit/public/platform/WebURLLoadTiming.h"
#include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
#include "third_party/WebKit/public/platform/WebURLRequest.h"
#include "third_party/WebKit/public/platform/WebURLResponse.h"
#include "third_party/WebKit/public/web/WebSecurityPolicy.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"

using base::Time;
using base::TimeTicks;
using blink::WebData;
using blink::WebHTTPBody;
using blink::WebHTTPHeaderVisitor;
using blink::WebHTTPLoadInfo;
using blink::WebReferrerPolicy;
using blink::WebSecurityPolicy;
using blink::WebString;
using blink::WebURL;
using blink::WebURLError;
using blink::WebURLLoader;
using blink::WebURLLoaderClient;
using blink::WebURLLoadTiming;
using blink::WebURLRequest;
using blink::WebURLResponse;

namespace content {

// Utilities ------------------------------------------------------------------

namespace {

    using HeadersVector = ResourceDevToolsInfo::HeadersVector;

    // TODO(estark): Figure out a way for the embedder to provide the
    // security style for a resource. Ideally, the logic for assigning
    // per-resource security styles should live in the same place as the
    // logic for assigning per-page security styles (which lives in the
    // embedder). It would also be nice for the embedder to have the chance
    // to control the per-resource security style beyond the simple logic
    // here. (For example, the embedder might want to mark certain resources
    // differently if they use SHA1 signatures.) https://crbug.com/648326
    blink::WebSecurityStyle GetSecurityStyleForResource(
        const GURL& url,
        net::CertStatus cert_status)
    {
        if (!url.SchemeIsCryptographic())
            return blink::WebSecurityStyleUnauthenticated;

        // Minor errors don't lower the security style to
        // WebSecurityStyleAuthenticationBroken.
        if (net::IsCertStatusError(cert_status) && !net::IsCertStatusMinorError(cert_status)) {
            return blink::WebSecurityStyleAuthenticationBroken;
        }

        return blink::WebSecurityStyleAuthenticated;
    }

    // Converts timing data from |load_timing| to the format used by WebKit.
    void PopulateURLLoadTiming(const net::LoadTimingInfo& load_timing,
        WebURLLoadTiming* url_timing)
    {
        DCHECK(!load_timing.request_start.is_null());

        const TimeTicks kNullTicks;
        url_timing->initialize();
        url_timing->setRequestTime(
            (load_timing.request_start - kNullTicks).InSecondsF());
        url_timing->setProxyStart(
            (load_timing.proxy_resolve_start - kNullTicks).InSecondsF());
        url_timing->setProxyEnd(
            (load_timing.proxy_resolve_end - kNullTicks).InSecondsF());
        url_timing->setDNSStart(
            (load_timing.connect_timing.dns_start - kNullTicks).InSecondsF());
        url_timing->setDNSEnd(
            (load_timing.connect_timing.dns_end - kNullTicks).InSecondsF());
        url_timing->setConnectStart(
            (load_timing.connect_timing.connect_start - kNullTicks).InSecondsF());
        url_timing->setConnectEnd(
            (load_timing.connect_timing.connect_end - kNullTicks).InSecondsF());
        url_timing->setSSLStart(
            (load_timing.connect_timing.ssl_start - kNullTicks).InSecondsF());
        url_timing->setSSLEnd(
            (load_timing.connect_timing.ssl_end - kNullTicks).InSecondsF());
        url_timing->setSendStart(
            (load_timing.send_start - kNullTicks).InSecondsF());
        url_timing->setSendEnd(
            (load_timing.send_end - kNullTicks).InSecondsF());
        url_timing->setReceiveHeadersEnd(
            (load_timing.receive_headers_end - kNullTicks).InSecondsF());
        url_timing->setPushStart((load_timing.push_start - kNullTicks).InSecondsF());
        url_timing->setPushEnd((load_timing.push_end - kNullTicks).InSecondsF());
    }

    net::RequestPriority ConvertWebKitPriorityToNetPriority(
        const WebURLRequest::Priority& priority)
    {
        switch (priority) {
        case WebURLRequest::PriorityVeryHigh:
            return net::HIGHEST;

        case WebURLRequest::PriorityHigh:
            return net::MEDIUM;

        case WebURLRequest::PriorityMedium:
            return net::LOW;

        case WebURLRequest::PriorityLow:
            return net::LOWEST;

        case WebURLRequest::PriorityVeryLow:
            return net::IDLE;

        case WebURLRequest::PriorityUnresolved:
        default:
            NOTREACHED();
            return net::LOW;
        }
    }

    blink::WebReferrerPolicy NetReferrerPolicyToBlinkReferrerPolicy(
        net::URLRequest::ReferrerPolicy net_policy)
    {
        switch (net_policy) {
        case net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
            return blink::WebReferrerPolicyNoReferrerWhenDowngrade;
        case net::URLRequest::
            REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN:
            return blink::
                WebReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin;
        case net::URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN:
            return blink::WebReferrerPolicyOriginWhenCrossOrigin;
        case net::URLRequest::NEVER_CLEAR_REFERRER:
            return blink::WebReferrerPolicyAlways;
        case net::URLRequest::ORIGIN:
            return blink::WebReferrerPolicyOrigin;
        case net::URLRequest::NO_REFERRER:
            return blink::WebReferrerPolicyNever;
        case net::URLRequest::MAX_REFERRER_POLICY:
            NOTREACHED();
            return blink::WebReferrerPolicyDefault;
        }
        NOTREACHED();
        return blink::WebReferrerPolicyDefault;
    }

    // Extracts info from a data scheme URL |url| into |info| and |data|. Returns
    // net::OK if successful. Returns a net error code otherwise.
    int GetInfoFromDataURL(const GURL& url,
        ResourceResponseInfo* info,
        std::string* data)
    {
        // Assure same time for all time fields of data: URLs.
        Time now = Time::Now();
        info->load_timing.request_start = TimeTicks::Now();
        info->load_timing.request_start_time = now;
        info->request_time = now;
        info->response_time = now;

        std::string mime_type;
        std::string charset;
        scoped_refptr<net::HttpResponseHeaders> headers(
            new net::HttpResponseHeaders(std::string()));
        int result = net::URLRequestDataJob::BuildResponse(
            url, &mime_type, &charset, data, headers.get());
        if (result != net::OK)
            return result;

        info->headers = headers;
        info->mime_type.swap(mime_type);
        info->charset.swap(charset);
        info->content_length = data->length();
        info->encoded_data_length = 0;
        info->encoded_body_length = 0;

        return net::OK;
    }

    // Convert a net::SignedCertificateTimestampAndStatus object to a
    // blink::WebURLResponse::SignedCertificateTimestamp object.
    blink::WebURLResponse::SignedCertificateTimestamp NetSCTToBlinkSCT(
        const net::SignedCertificateTimestampAndStatus& sct_and_status)
    {
        return blink::WebURLResponse::SignedCertificateTimestamp(
            WebString::fromASCII(net::ct::StatusToString(sct_and_status.status)),
            WebString::fromASCII(net::ct::OriginToString(sct_and_status.sct->origin)),
            WebString::fromUTF8(sct_and_status.sct->log_description),
            WebString::fromASCII(
                base::HexEncode(sct_and_status.sct->log_id.c_str(),
                    sct_and_status.sct->log_id.length())),
            sct_and_status.sct->timestamp.ToJavaTime(),
            WebString::fromASCII(net::ct::HashAlgorithmToString(
                sct_and_status.sct->signature.hash_algorithm)),
            WebString::fromASCII(net::ct::SignatureAlgorithmToString(
                sct_and_status.sct->signature.signature_algorithm)),
            WebString::fromASCII(base::HexEncode(
                sct_and_status.sct->signature.signature_data.c_str(),
                sct_and_status.sct->signature.signature_data.length())));
    }

    void SetSecurityStyleAndDetails(const GURL& url,
        const ResourceResponseInfo& info,
        WebURLResponse* response,
        bool report_security_info)
    {
        if (!report_security_info) {
            response->setSecurityStyle(blink::WebSecurityStyleUnknown);
            return;
        }
        if (!url.SchemeIsCryptographic()) {
            response->setSecurityStyle(blink::WebSecurityStyleUnauthenticated);
            return;
        }

        // There are cases where an HTTPS request can come in without security
        // info attached (such as a redirect response).
        if (info.certificate.empty()) {
            response->setSecurityStyle(blink::WebSecurityStyleUnknown);
            return;
        }

        int ssl_version = net::SSLConnectionStatusToVersion(info.ssl_connection_status);
        const char* protocol;
        net::SSLVersionToString(&protocol, ssl_version);

        const char* key_exchange;
        const char* cipher;
        const char* mac;
        bool is_aead;
        bool is_tls13;
        uint16_t cipher_suite = net::SSLConnectionStatusToCipherSuite(info.ssl_connection_status);
        net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, &is_aead,
            &is_tls13, cipher_suite);
        if (key_exchange == nullptr) {
            DCHECK(is_tls13);
            key_exchange = "";
        }

        if (mac == nullptr) {
            DCHECK(is_aead);
            mac = "";
        }

        const char* key_exchange_group = "";
        if (info.ssl_key_exchange_group != 0) {
            // Historically the field was named 'curve' rather than 'group'.
            key_exchange_group = SSL_get_curve_name(info.ssl_key_exchange_group);
            if (!key_exchange_group) {
                NOTREACHED();
                key_exchange_group = "";
            }
        }

        response->setSecurityStyle(
            GetSecurityStyleForResource(url, info.cert_status));

        blink::WebURLResponse::SignedCertificateTimestampList sct_list(
            info.signed_certificate_timestamps.size());

        for (size_t i = 0; i < sct_list.size(); ++i)
            sct_list[i] = NetSCTToBlinkSCT(info.signed_certificate_timestamps[i]);

        std::string subject, issuer;
        base::Time valid_start, valid_expiry;
        std::vector<std::string> san;
        bool rv = net::x509_util::ParseCertificateSandboxed(
            info.certificate[0], &subject, &issuer, &valid_start, &valid_expiry, &san,
            &san);
        if (!rv) {
            NOTREACHED();
            response->setSecurityStyle(blink::WebSecurityStyleUnknown);
            return;
        }

        blink::WebVector<blink::WebString> web_san(san.size());
        std::transform(
            san.begin(),
            san.end(), web_san.begin(),
            [](const std::string& h) { return blink::WebString::fromLatin1(h); });

        blink::WebVector<blink::WebString> web_cert(info.certificate.size());
        std::transform(
            info.certificate.begin(),
            info.certificate.end(), web_cert.begin(),
            [](const std::string& h) { return blink::WebString::fromLatin1(h); });

        blink::WebURLResponse::WebSecurityDetails webSecurityDetails(
            WebString::fromASCII(protocol), WebString::fromASCII(key_exchange),
            WebString::fromASCII(key_exchange_group), WebString::fromASCII(cipher),
            WebString::fromASCII(mac), WebString::fromUTF8(subject), web_san,
            WebString::fromUTF8(issuer), valid_start.ToDoubleT(),
            valid_expiry.ToDoubleT(), web_cert, sct_list);

        response->setSecurityDetails(webSecurityDetails);
    }

} // namespace

StreamOverrideParameters::StreamOverrideParameters() { }
StreamOverrideParameters::~StreamOverrideParameters() { }

// This inner class exists since the WebURLLoader may be deleted while inside a
// call to WebURLLoaderClient.  Refcounting is to keep the context from being
// deleted if it may have work to do after calling into the client.
class WebURLLoaderImpl::Context : public base::RefCounted<Context> {
public:
    using ReceivedData = RequestPeer::ReceivedData;

    Context(WebURLLoaderImpl* loader,
        ResourceDispatcher* resource_dispatcher,
        mojom::URLLoaderFactory* factory,
        mojo::AssociatedGroup* associated_group);

    WebURLLoaderClient* client() const { return client_; }
    void set_client(WebURLLoaderClient* client) { client_ = client; }

    void Cancel();
    void SetDefersLoading(bool value);
    void DidChangePriority(WebURLRequest::Priority new_priority,
        int intra_priority_value);
    void Start(const WebURLRequest& request,
        SyncLoadResponse* sync_load_response);
    void SetTaskRunner(
        const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);

    void OnUploadProgress(uint64_t position, uint64_t size);
    bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
        const ResourceResponseInfo& info);
    void OnReceivedResponse(const ResourceResponseInfo& info);
    void OnDownloadedData(int len, int encoded_data_length);
    void OnReceivedData(std::unique_ptr<ReceivedData> data);
    void OnTransferSizeUpdated(int transfer_size_diff);
    void OnReceivedCachedMetadata(const char* data, int len);
    void OnCompletedRequest(int error_code,
        bool was_ignored_by_handler,
        bool stale_copy_in_cache,
        const base::TimeTicks& completion_time,
        int64_t total_transfer_size,
        int64_t encoded_body_size);

private:
    friend class base::RefCounted<Context>;
    ~Context();

    // Called when the body data stream is detached from the reader side.
    void CancelBodyStreaming();
    // We can optimize the handling of data URLs in most cases.
    bool CanHandleDataURLRequestLocally() const;
    void HandleDataURL();

    WebURLLoaderImpl* loader_;
    WebURLRequest request_;
    WebURLLoaderClient* client_;
    ResourceDispatcher* resource_dispatcher_;
    scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
    std::unique_ptr<FtpDirectoryListingResponseDelegate> ftp_listing_delegate_;
    std::unique_ptr<StreamOverrideParameters> stream_override_;
    std::unique_ptr<SharedMemoryDataConsumerHandle::Writer> body_stream_writer_;
    enum DeferState { NOT_DEFERRING,
        SHOULD_DEFER,
        DEFERRED_DATA };
    DeferState defers_loading_;
    int request_id_;

    // These are owned by the Blink::Platform singleton.
    mojom::URLLoaderFactory* url_loader_factory_;
    mojo::AssociatedGroup* associated_group_;
};

// A thin wrapper class for Context to ensure its lifetime while it is
// handling IPC messages coming from ResourceDispatcher. Owns one ref to
// Context and held by ResourceDispatcher.
class WebURLLoaderImpl::RequestPeerImpl : public RequestPeer {
public:
    explicit RequestPeerImpl(Context* context);

    // RequestPeer methods:
    void OnUploadProgress(uint64_t position, uint64_t size) override;
    bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
        const ResourceResponseInfo& info) override;
    void OnReceivedResponse(const ResourceResponseInfo& info) override;
    void OnDownloadedData(int len, int encoded_data_length) override;
    void OnReceivedData(std::unique_ptr<ReceivedData> data) override;
    void OnTransferSizeUpdated(int transfer_size_diff) override;
    void OnReceivedCachedMetadata(const char* data, int len) override;
    void OnCompletedRequest(int error_code,
        bool was_ignored_by_handler,
        bool stale_copy_in_cache,
        const base::TimeTicks& completion_time,
        int64_t total_transfer_size,
        int64_t encoded_body_size) override;

private:
    scoped_refptr<Context> context_;
    DISALLOW_COPY_AND_ASSIGN(RequestPeerImpl);
};

// WebURLLoaderImpl::Context --------------------------------------------------

WebURLLoaderImpl::Context::Context(WebURLLoaderImpl* loader,
    ResourceDispatcher* resource_dispatcher,
    mojom::URLLoaderFactory* url_loader_factory,
    mojo::AssociatedGroup* associated_group)
    : loader_(loader)
    , client_(NULL)
    , resource_dispatcher_(resource_dispatcher)
    , task_runner_(base::ThreadTaskRunnerHandle::Get())
    , defers_loading_(NOT_DEFERRING)
    , request_id_(-1)
    , url_loader_factory_(url_loader_factory)
    , associated_group_(associated_group)
{
}

void WebURLLoaderImpl::Context::Cancel()
{
    TRACE_EVENT_WITH_FLOW0("loading", "WebURLLoaderImpl::Context::Cancel", this,
        TRACE_EVENT_FLAG_FLOW_IN);
    if (resource_dispatcher_ && // NULL in unittest.
        request_id_ != -1) {
        resource_dispatcher_->Cancel(request_id_);
        request_id_ = -1;
    }

    if (body_stream_writer_)
        body_stream_writer_->Fail();

    // Ensure that we do not notify the delegate anymore as it has
    // its own pointer to the client.
    if (ftp_listing_delegate_)
        ftp_listing_delegate_->Cancel();

    // Do not make any further calls to the client.
    client_ = NULL;
    loader_ = NULL;
}

void WebURLLoaderImpl::Context::SetDefersLoading(bool value)
{
    if (request_id_ != -1)
        resource_dispatcher_->SetDefersLoading(request_id_, value);
    if (value && defers_loading_ == NOT_DEFERRING) {
        defers_loading_ = SHOULD_DEFER;
    } else if (!value && defers_loading_ != NOT_DEFERRING) {
        if (defers_loading_ == DEFERRED_DATA) {
            task_runner_->PostTask(FROM_HERE,
                base::Bind(&Context::HandleDataURL, this));
        }
        defers_loading_ = NOT_DEFERRING;
    }
}

void WebURLLoaderImpl::Context::DidChangePriority(
    WebURLRequest::Priority new_priority, int intra_priority_value)
{
    if (request_id_ != -1) {
        resource_dispatcher_->DidChangePriority(
            request_id_,
            ConvertWebKitPriorityToNetPriority(new_priority),
            intra_priority_value);
    }
}

void WebURLLoaderImpl::Context::Start(const WebURLRequest& request,
    SyncLoadResponse* sync_load_response)
{
    DCHECK(request_id_ == -1);
    request_ = request; // Save the request.
    GURL url = request.url();

    if (CanHandleDataURLRequestLocally()) {
        if (sync_load_response) {
            // This is a sync load. Do the work now.
            sync_load_response->url = url;
            sync_load_response->error_code = GetInfoFromDataURL(sync_load_response->url, sync_load_response,
                &sync_load_response->data);
        } else {
            task_runner_->PostTask(FROM_HERE,
                base::Bind(&Context::HandleDataURL, this));
        }
        return;
    }

    if (request.getExtraData()) {
        RequestExtraData* extra_data = static_cast<RequestExtraData*>(request.getExtraData());
        stream_override_ = extra_data->TakeStreamOverrideOwnership();
    }

    // PlzNavigate: outside of tests, the only navigation requests going through
    // the WebURLLoader are the ones created by CommitNavigation. Several browser
    // tests load HTML directly through a data url which will be handled by the
    // block above.
    DCHECK(!IsBrowserSideNavigationEnabled() || stream_override_.get() || request.getFrameType() == WebURLRequest::FrameTypeNone);

    GURL referrer_url(
        request.httpHeaderField(WebString::fromASCII("Referer")).latin1());
    const std::string& method = request.httpMethod().latin1();

    // TODO(brettw) this should take parameter encoding into account when
    // creating the GURLs.

    // TODO(horo): Check credentials flag is unset when credentials mode is omit.
    //             Check credentials flag is set when credentials mode is include.

    std::unique_ptr<ResourceRequest> resource_request(new ResourceRequest);

    resource_request->method = method;
    resource_request->url = url;
    resource_request->first_party_for_cookies = request.firstPartyForCookies();
    resource_request->request_initiator = request.requestorOrigin().isNull()
        ? base::Optional<url::Origin>()
        : base::Optional<url::Origin>(request.requestorOrigin());
    resource_request->referrer = referrer_url;

    resource_request->referrer_policy = request.getReferrerPolicy();

    resource_request->headers = GetWebURLRequestHeaders(request);
    resource_request->load_flags = GetLoadFlagsForWebURLRequest(request);
    // origin_pid only needs to be non-zero if the request originates outside
    // the render process, so we can use requestorProcessID even for requests
    // from in-process plugins.
    resource_request->origin_pid = request.requestorProcessID();
    resource_request->resource_type = WebURLRequestToResourceType(request);
    resource_request->priority = ConvertWebKitPriorityToNetPriority(request.getPriority());
    resource_request->appcache_host_id = request.appCacheHostID();
    resource_request->should_reset_appcache = request.shouldResetAppCache();
    resource_request->skip_service_worker = GetSkipServiceWorkerForWebURLRequest(request);
    resource_request->fetch_request_mode = GetFetchRequestModeForWebURLRequest(request);
    resource_request->fetch_credentials_mode = GetFetchCredentialsModeForWebURLRequest(request);
    resource_request->fetch_redirect_mode = GetFetchRedirectModeForWebURLRequest(request);
    resource_request->fetch_request_context_type = GetRequestContextTypeForWebURLRequest(request);
    resource_request->fetch_mixed_content_context_type = GetMixedContentContextTypeForWebURLRequest(request);

    resource_request->fetch_frame_type = GetRequestContextFrameTypeForWebURLRequest(request);
    resource_request->request_body = GetRequestBodyForWebURLRequest(request).get();
    resource_request->download_to_file = request.downloadToFile();
    resource_request->has_user_gesture = request.hasUserGesture();
    resource_request->enable_load_timing = true;
    resource_request->enable_upload_progress = request.reportUploadProgress();
    if (request.getRequestContext() == WebURLRequest::RequestContextXMLHttpRequest && (url.has_username() || url.has_password())) {
        resource_request->do_not_prompt_for_login = true;
    }
    resource_request->report_raw_headers = request.reportRawHeaders();
    resource_request->previews_state = static_cast<PreviewsState>(request.getPreviewsState());

    // PlzNavigate: during navigation, the renderer should request a stream which
    // contains the body of the response. The network request has already been
    // made by the browser.
    if (stream_override_.get()) {
        CHECK(IsBrowserSideNavigationEnabled());
        DCHECK(!sync_load_response);
        DCHECK_NE(WebURLRequest::FrameTypeNone, request.getFrameType());
        resource_request->resource_body_stream_url = stream_override_->stream_url;
    }

    const RequestExtraData empty_extra_data;
    const RequestExtraData* extra_data;
    if (request.getExtraData())
        extra_data = static_cast<RequestExtraData*>(request.getExtraData());
    else
        extra_data = &empty_extra_data;
    extra_data->CopyToResourceRequest(resource_request.get());

    if (sync_load_response) {
        DCHECK(defers_loading_ == NOT_DEFERRING);
        resource_dispatcher_->StartSync(
            std::move(resource_request), request.requestorID(), sync_load_response,
            request.getLoadingIPCType(), url_loader_factory_);
        return;
    }

    TRACE_EVENT_WITH_FLOW0("loading", "WebURLLoaderImpl::Context::Start", this,
        TRACE_EVENT_FLAG_FLOW_OUT);
    request_id_ = resource_dispatcher_->StartAsync(
        std::move(resource_request), request.requestorID(), task_runner_,
        extra_data->frame_origin(),
        base::MakeUnique<WebURLLoaderImpl::RequestPeerImpl>(this),
        request.getLoadingIPCType(), url_loader_factory_, associated_group_);

    if (defers_loading_ != NOT_DEFERRING)
        resource_dispatcher_->SetDefersLoading(request_id_, true);
}

void WebURLLoaderImpl::Context::SetTaskRunner(
    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
{
    task_runner_ = task_runner;
}

void WebURLLoaderImpl::Context::OnUploadProgress(uint64_t position,
    uint64_t size)
{
    if (client_)
        client_->didSendData(position, size);
}

bool WebURLLoaderImpl::Context::OnReceivedRedirect(
    const net::RedirectInfo& redirect_info,
    const ResourceResponseInfo& info)
{
    if (!client_)
        return false;

    TRACE_EVENT_WITH_FLOW0(
        "loading", "WebURLLoaderImpl::Context::OnReceivedRedirect",
        this, TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);

    WebURLResponse response;
    PopulateURLResponse(request_.url(), info, &response,
        request_.reportRawHeaders());

    WebURLRequest new_request = PopulateURLRequestForRedirect(
        request_, redirect_info,
        info.was_fetched_via_service_worker
            ? blink::WebURLRequest::SkipServiceWorker::None
            : blink::WebURLRequest::SkipServiceWorker::All);

    bool follow = client_->willFollowRedirect(new_request, response);
    if (!follow) {
        request_ = WebURLRequest();

        return false;
    }

    DCHECK(WebURL(redirect_info.new_url) == new_request.url());

    request_ = new_request;

    // First-party cookie logic moved from DocumentLoader in Blink to
    // net::URLRequest in the browser. Assert that Blink didn't try to change it
    // to something else.
    DCHECK_EQ(redirect_info.new_first_party_for_cookies.spec(),
        request_.firstPartyForCookies().string().utf8());
    return true;
}

void WebURLLoaderImpl::Context::OnReceivedResponse(
    const ResourceResponseInfo& initial_info)
{
    if (!client_)
        return;

    TRACE_EVENT_WITH_FLOW0(
        "loading", "WebURLLoaderImpl::Context::OnReceivedResponse",
        this, TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);

    ResourceResponseInfo info = initial_info;

    // PlzNavigate: during navigations, the ResourceResponse has already been
    // received on the browser side, and has been passed down to the renderer.
    if (stream_override_.get()) {
        CHECK(IsBrowserSideNavigationEnabled());
        // Compute the delta between the response sizes so that the accurate
        // transfer size can be reported at the end of the request.
        stream_override_->total_transfer_size_delta = stream_override_->response.encoded_data_length - initial_info.encoded_data_length;
        info = stream_override_->response;
    }

    WebURLResponse response;
    PopulateURLResponse(request_.url(), info, &response,
        request_.reportRawHeaders());

    if (stream_override_.get()) {
        CHECK(IsBrowserSideNavigationEnabled());
        DCHECK(stream_override_->redirect_responses.size() == stream_override_->redirects.size());
        for (size_t i = 0; i < stream_override_->redirects.size(); ++i) {
            WebURLResponse previous_response;
            // TODO(arthursonzogni) Once Devtool is supported by PlzNavigate, the
            // |report_raw_header| argument must be checked.
            WebURLLoaderImpl::PopulateURLResponse(
                stream_override_->redirects[i],
                stream_override_->redirect_responses[i],
                &previous_response,
                request_.reportRawHeaders());
            response.appendRedirectResponse(previous_response);
        }
    }

    bool show_raw_listing = (GURL(request_.url()).query() == "raw");

    if (info.mime_type == "text/vnd.chromium.ftp-dir") {
        if (show_raw_listing) {
            // Set the MIME type to plain text to prevent any active content.
            response.setMIMEType("text/plain");
        } else {
            // We're going to produce a parsed listing in HTML.
            response.setMIMEType("text/html");
        }
    }
    if (info.headers.get() && info.mime_type == "multipart/x-mixed-replace") {
        std::string content_type;
        info.headers->EnumerateHeader(NULL, "content-type", &content_type);

        std::string mime_type;
        std::string charset;
        bool had_charset = false;
        std::string boundary;
        net::HttpUtil::ParseContentType(content_type, &mime_type, &charset,
            &had_charset, &boundary);
        base::TrimString(boundary, " \"", &boundary);
        response.setMultipartBoundary(boundary.data(), boundary.size());
    }

    if (request_.useStreamOnResponse()) {
        SharedMemoryDataConsumerHandle::BackpressureMode mode = SharedMemoryDataConsumerHandle::kDoNotApplyBackpressure;
        if (info.headers && info.headers->HasHeaderValue("Cache-Control", "no-store")) {
            mode = SharedMemoryDataConsumerHandle::kApplyBackpressure;
        }

        auto read_handle = base::MakeUnique<SharedMemoryDataConsumerHandle>(
            mode, base::Bind(&Context::CancelBodyStreaming, this),
            &body_stream_writer_);

        // Here |body_stream_writer_| has an indirect reference to |this| and that
        // creates a reference cycle, but it is not a problem because the cycle
        // will break if one of the following happens:
        //  1) The body data transfer is done (with or without an error).
        //  2) |read_handle| (and its reader) is detached.
        client_->didReceiveResponse(response, std::move(read_handle));
        // TODO(yhirano): Support ftp listening and multipart
        return;
    } else {
        client_->didReceiveResponse(response);
    }

    // We may have been cancelled after didReceiveResponse, which would leave us
    // without a client and therefore without much need to do further handling.
    if (!client_)
        return;

    DCHECK(!ftp_listing_delegate_.get());
    if (info.mime_type == "text/vnd.chromium.ftp-dir" && !show_raw_listing) {
        ftp_listing_delegate_.reset(
            new FtpDirectoryListingResponseDelegate(client_, loader_, response));
    }
}

void WebURLLoaderImpl::Context::OnDownloadedData(int len,
    int encoded_data_length)
{
    if (client_)
        client_->didDownloadData(len, encoded_data_length);
}

void WebURLLoaderImpl::Context::OnReceivedData(
    std::unique_ptr<ReceivedData> data)
{
    const char* payload = data->payload();
    int data_length = data->length();
    if (!client_)
        return;

    TRACE_EVENT_WITH_FLOW0(
        "loading", "WebURLLoaderImpl::Context::OnReceivedData",
        this, TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);

    if (ftp_listing_delegate_) {
        // The FTP listing delegate will make the appropriate calls to
        // client_->didReceiveData and client_->didReceiveResponse.
        ftp_listing_delegate_->OnReceivedData(payload, data_length);
    } else {
        // We dispatch the data even when |useStreamOnResponse()| is set, in order
        // to make Devtools work.
        client_->didReceiveData(payload, data_length);

        if (request_.useStreamOnResponse()) {
            // We don't support ftp_listening_delegate_ for now.
            // TODO(yhirano): Support ftp listening.
            body_stream_writer_->AddData(std::move(data));
        }
    }
}

void WebURLLoaderImpl::Context::OnTransferSizeUpdated(int transfer_size_diff)
{
    client_->didReceiveTransferSizeUpdate(transfer_size_diff);
}

void WebURLLoaderImpl::Context::OnReceivedCachedMetadata(
    const char* data, int len)
{
    if (!client_)
        return;
    TRACE_EVENT_WITH_FLOW0(
        "loading", "WebURLLoaderImpl::Context::OnReceivedCachedMetadata",
        this, TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
    client_->didReceiveCachedMetadata(data, len);
}

void WebURLLoaderImpl::Context::OnCompletedRequest(
    int error_code,
    bool was_ignored_by_handler,
    bool stale_copy_in_cache,
    const base::TimeTicks& completion_time,
    int64_t total_transfer_size,
    int64_t encoded_body_size)
{
    if (ftp_listing_delegate_) {
        ftp_listing_delegate_->OnCompletedRequest();
        ftp_listing_delegate_.reset(NULL);
    }

    if (body_stream_writer_ && error_code != net::OK)
        body_stream_writer_->Fail();
    body_stream_writer_.reset();

    if (client_) {
        TRACE_EVENT_WITH_FLOW0(
            "loading", "WebURLLoaderImpl::Context::OnCompletedRequest",
            this, TRACE_EVENT_FLAG_FLOW_IN);

        if (error_code != net::OK) {
            client_->didFail(CreateWebURLError(request_.url(), stale_copy_in_cache,
                                 error_code, was_ignored_by_handler),
                total_transfer_size, encoded_body_size);
        } else {
            // PlzNavigate: compute the accurate transfer size for navigations.
            if (stream_override_.get()) {
                DCHECK(IsBrowserSideNavigationEnabled());
                total_transfer_size += stream_override_->total_transfer_size_delta;
            }

            client_->didFinishLoading((completion_time - TimeTicks()).InSecondsF(),
                total_transfer_size, encoded_body_size);
        }
    }
}

WebURLLoaderImpl::Context::~Context()
{
    // We must be already cancelled at this point.
    DCHECK_LT(request_id_, 0);
}

void WebURLLoaderImpl::Context::CancelBodyStreaming()
{
    scoped_refptr<Context> protect(this);

    // Notify renderer clients that the request is canceled.
    if (ftp_listing_delegate_) {
        ftp_listing_delegate_->OnCompletedRequest();
        ftp_listing_delegate_.reset(NULL);
    }

    if (body_stream_writer_) {
        body_stream_writer_->Fail();
        body_stream_writer_.reset();
    }
    if (client_) {
        // TODO(yhirano): Set |stale_copy_in_cache| appropriately if possible.
        client_->didFail(CreateWebURLError(request_.url(), false, net::ERR_ABORTED),
            WebURLLoaderClient::kUnknownEncodedDataLength, 0);
    }

    // Notify the browser process that the request is canceled.
    Cancel();
}

bool WebURLLoaderImpl::Context::CanHandleDataURLRequestLocally() const
{
    GURL url = request_.url();
    if (!url.SchemeIs(url::kDataScheme))
        return false;

    // The fast paths for data URL, Start() and HandleDataURL(), don't support
    // the downloadToFile option.
    if (request_.downloadToFile())
        return false;

    // Data url requests from object tags may need to be intercepted as streams
    // and so need to be sent to the browser.
    if (request_.getRequestContext() == WebURLRequest::RequestContextObject)
        return false;

        // Optimize for the case where we can handle a data URL locally.  We must
        // skip this for data URLs targetted at frames since those could trigger a
        // download.
        //
        // NOTE: We special case MIME types we can render both for performance
        // reasons as well as to support unit tests.

#if defined(OS_ANDROID)
    // For compatibility reasons on Android we need to expose top-level data://
    // to the browser. In tests resource_dispatcher_ can be null, and test pages
    // need to be loaded locally.
    if (resource_dispatcher_ && request_.getFrameType() == WebURLRequest::FrameTypeTopLevel)
        return false;
#endif

    if (request_.getFrameType() != WebURLRequest::FrameTypeTopLevel && request_.getFrameType() != WebURLRequest::FrameTypeNested)
        return true;

    std::string mime_type, unused_charset;
    if (net::DataURL::Parse(request_.url(), &mime_type, &unused_charset, NULL) && mime_util::IsSupportedMimeType(mime_type))
        return true;

    return false;
}

void WebURLLoaderImpl::Context::HandleDataURL()
{
    DCHECK_NE(defers_loading_, DEFERRED_DATA);
    if (defers_loading_ == SHOULD_DEFER) {
        defers_loading_ = DEFERRED_DATA;
        return;
    }

    ResourceResponseInfo info;
    std::string data;

    int error_code = GetInfoFromDataURL(request_.url(), &info, &data);

    if (error_code == net::OK) {
        OnReceivedResponse(info);
        auto size = data.size();
        if (size != 0)
            OnReceivedData(base::MakeUnique<FixedReceivedData>(data.data(), size));
    }

    OnCompletedRequest(error_code, false, false, base::TimeTicks::Now(), 0,
        data.size());
}

// WebURLLoaderImpl::RequestPeerImpl ------------------------------------------

WebURLLoaderImpl::RequestPeerImpl::RequestPeerImpl(Context* context)
    : context_(context)
{
}

void WebURLLoaderImpl::RequestPeerImpl::OnUploadProgress(uint64_t position,
    uint64_t size)
{
    context_->OnUploadProgress(position, size);
}

bool WebURLLoaderImpl::RequestPeerImpl::OnReceivedRedirect(
    const net::RedirectInfo& redirect_info,
    const ResourceResponseInfo& info)
{
    return context_->OnReceivedRedirect(redirect_info, info);
}

void WebURLLoaderImpl::RequestPeerImpl::OnReceivedResponse(
    const ResourceResponseInfo& info)
{
    context_->OnReceivedResponse(info);
}

void WebURLLoaderImpl::RequestPeerImpl::OnDownloadedData(
    int len,
    int encoded_data_length)
{
    context_->OnDownloadedData(len, encoded_data_length);
}

void WebURLLoaderImpl::RequestPeerImpl::OnReceivedData(
    std::unique_ptr<ReceivedData> data)
{
    context_->OnReceivedData(std::move(data));
}

void WebURLLoaderImpl::RequestPeerImpl::OnTransferSizeUpdated(
    int transfer_size_diff)
{
    context_->OnTransferSizeUpdated(transfer_size_diff);
}

void WebURLLoaderImpl::RequestPeerImpl::OnReceivedCachedMetadata(
    const char* data,
    int len)
{
    context_->OnReceivedCachedMetadata(data, len);
}

void WebURLLoaderImpl::RequestPeerImpl::OnCompletedRequest(
    int error_code,
    bool was_ignored_by_handler,
    bool stale_copy_in_cache,
    const base::TimeTicks& completion_time,
    int64_t total_transfer_size,
    int64_t encoded_body_size)
{
    context_->OnCompletedRequest(error_code, was_ignored_by_handler,
        stale_copy_in_cache, completion_time,
        total_transfer_size, encoded_body_size);
}

// WebURLLoaderImpl -----------------------------------------------------------

WebURLLoaderImpl::WebURLLoaderImpl(ResourceDispatcher* resource_dispatcher,
    mojom::URLLoaderFactory* url_loader_factory,
    mojo::AssociatedGroup* associated_group)
    : context_(new Context(this,
        resource_dispatcher,
        url_loader_factory,
        associated_group))
{
}

WebURLLoaderImpl::~WebURLLoaderImpl()
{
    cancel();
}

void WebURLLoaderImpl::PopulateURLResponse(const GURL& url,
    const ResourceResponseInfo& info,
    WebURLResponse* response,
    bool report_security_info)
{
    response->setURL(url);
    response->setResponseTime(info.response_time.ToInternalValue());
    response->setMIMEType(WebString::fromUTF8(info.mime_type));
    response->setTextEncodingName(WebString::fromUTF8(info.charset));
    response->setExpectedContentLength(info.content_length);
    response->setHasMajorCertificateErrors(info.has_major_certificate_errors);
    response->setAppCacheID(info.appcache_id);
    response->setAppCacheManifestURL(info.appcache_manifest_url);
    response->setWasCached(!info.load_timing.request_start_time.is_null() && info.response_time < info.load_timing.request_start_time);
    response->setRemoteIPAddress(
        WebString::fromUTF8(info.socket_address.HostForURL()));
    response->setRemotePort(info.socket_address.port());
    response->setConnectionID(info.load_timing.socket_log_id);
    response->setConnectionReused(info.load_timing.socket_reused);
    response->setDownloadFilePath(
        blink::FilePathToWebString(info.download_file_path));
    response->setWasFetchedViaSPDY(info.was_fetched_via_spdy);
    response->setWasFetchedViaServiceWorker(info.was_fetched_via_service_worker);
    response->setWasFetchedViaForeignFetch(info.was_fetched_via_foreign_fetch);
    response->setWasFallbackRequiredByServiceWorker(
        info.was_fallback_required_by_service_worker);
    response->setServiceWorkerResponseType(info.response_type_via_service_worker);
    response->setURLListViaServiceWorker(info.url_list_via_service_worker);
    response->setCacheStorageCacheName(
        info.is_in_cache_storage
            ? blink::WebString::fromUTF8(info.cache_storage_cache_name)
            : blink::WebString());
    blink::WebVector<blink::WebString> cors_exposed_header_names(
        info.cors_exposed_header_names.size());
    std::transform(
        info.cors_exposed_header_names.begin(),
        info.cors_exposed_header_names.end(), cors_exposed_header_names.begin(),
        [](const std::string& h) { return blink::WebString::fromLatin1(h); });
    response->setCorsExposedHeaderNames(cors_exposed_header_names);
    response->setDidServiceWorkerNavigationPreload(
        info.did_service_worker_navigation_preload);
    response->setEncodedDataLength(info.encoded_data_length);

    SetSecurityStyleAndDetails(url, info, response, report_security_info);

    WebURLResponseExtraDataImpl* extra_data = new WebURLResponseExtraDataImpl(info.alpn_negotiated_protocol);
    response->setExtraData(extra_data);
    extra_data->set_was_fetched_via_spdy(info.was_fetched_via_spdy);
    extra_data->set_was_alpn_negotiated(info.was_alpn_negotiated);
    extra_data->set_was_alternate_protocol_available(
        info.was_alternate_protocol_available);
    extra_data->set_connection_info(info.connection_info);
    extra_data->set_previews_state(info.previews_state);
    extra_data->set_effective_connection_type(info.effective_connection_type);

    // If there's no received headers end time, don't set load timing.  This is
    // the case for non-HTTP requests, requests that don't go over the wire, and
    // certain error cases.
    if (!info.load_timing.receive_headers_end.is_null()) {
        WebURLLoadTiming timing;
        PopulateURLLoadTiming(info.load_timing, &timing);
        const TimeTicks kNullTicks;
        timing.setWorkerStart(
            (info.service_worker_start_time - kNullTicks).InSecondsF());
        timing.setWorkerReady(
            (info.service_worker_ready_time - kNullTicks).InSecondsF());
        response->setLoadTiming(timing);
    }

    if (info.devtools_info.get()) {
        WebHTTPLoadInfo load_info;

        load_info.setHTTPStatusCode(info.devtools_info->http_status_code);
        load_info.setHTTPStatusText(WebString::fromLatin1(
            info.devtools_info->http_status_text));

        load_info.setRequestHeadersText(WebString::fromLatin1(
            info.devtools_info->request_headers_text));
        load_info.setResponseHeadersText(WebString::fromLatin1(
            info.devtools_info->response_headers_text));
        const HeadersVector& request_headers = info.devtools_info->request_headers;
        for (HeadersVector::const_iterator it = request_headers.begin();
             it != request_headers.end(); ++it) {
            load_info.addRequestHeader(WebString::fromLatin1(it->first),
                WebString::fromLatin1(it->second));
        }
        const HeadersVector& response_headers = info.devtools_info->response_headers;
        for (HeadersVector::const_iterator it = response_headers.begin();
             it != response_headers.end(); ++it) {
            load_info.addResponseHeader(WebString::fromLatin1(it->first),
                WebString::fromLatin1(it->second));
        }
        load_info.setNPNNegotiatedProtocol(
            WebString::fromLatin1(info.alpn_negotiated_protocol));
        response->setHTTPLoadInfo(load_info);
    }

    const net::HttpResponseHeaders* headers = info.headers.get();
    if (!headers)
        return;

    WebURLResponse::HTTPVersion version = WebURLResponse::HTTPVersionUnknown;
    if (headers->GetHttpVersion() == net::HttpVersion(0, 9))
        version = WebURLResponse::HTTPVersion_0_9;
    else if (headers->GetHttpVersion() == net::HttpVersion(1, 0))
        version = WebURLResponse::HTTPVersion_1_0;
    else if (headers->GetHttpVersion() == net::HttpVersion(1, 1))
        version = WebURLResponse::HTTPVersion_1_1;
    else if (headers->GetHttpVersion() == net::HttpVersion(2, 0))
        version = WebURLResponse::HTTPVersion_2_0;
    response->setHTTPVersion(version);
    response->setHTTPStatusCode(headers->response_code());
    response->setHTTPStatusText(WebString::fromLatin1(headers->GetStatusText()));

    // TODO(darin): We should leverage HttpResponseHeaders for this, and this
    // should be using the same code as ResourceDispatcherHost.
    // TODO(jungshik): Figure out the actual value of the referrer charset and
    // pass it to GetSuggestedFilename.
    std::string value;
    headers->EnumerateHeader(NULL, "content-disposition", &value);
    response->setSuggestedFileName(blink::WebString::fromUTF16(
        net::GetSuggestedFilename(url, value,
            std::string(), // referrer_charset
            std::string(), // suggested_name
            std::string(), // mime_type
            std::string()))); // default_name

    Time time_val;
    if (headers->GetLastModifiedValue(&time_val))
        response->setLastModifiedDate(time_val.ToDoubleT());

    // Build up the header map.
    size_t iter = 0;
    std::string name;
    while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
        response->addHTTPHeaderField(WebString::fromLatin1(name),
            WebString::fromLatin1(value));
    }
}

WebURLRequest WebURLLoaderImpl::PopulateURLRequestForRedirect(
    const blink::WebURLRequest& request,
    const net::RedirectInfo& redirect_info,
    blink::WebURLRequest::SkipServiceWorker skip_service_worker)
{
    // TODO(darin): We lack sufficient information to construct the actual
    // request that resulted from the redirect.
    WebURLRequest new_request(redirect_info.new_url);
    new_request.setFirstPartyForCookies(
        redirect_info.new_first_party_for_cookies);
    new_request.setDownloadToFile(request.downloadToFile());
    new_request.setUseStreamOnResponse(request.useStreamOnResponse());
    new_request.setRequestContext(request.getRequestContext());
    new_request.setFrameType(request.getFrameType());
    new_request.setSkipServiceWorker(skip_service_worker);
    new_request.setShouldResetAppCache(request.shouldResetAppCache());
    new_request.setFetchRequestMode(request.getFetchRequestMode());
    new_request.setFetchCredentialsMode(request.getFetchCredentialsMode());

    new_request.setHTTPReferrer(WebString::fromUTF8(redirect_info.new_referrer),
        NetReferrerPolicyToBlinkReferrerPolicy(
            redirect_info.new_referrer_policy));
    new_request.setPriority(request.getPriority());

    std::string old_method = request.httpMethod().utf8();
    new_request.setHTTPMethod(WebString::fromUTF8(redirect_info.new_method));
    if (redirect_info.new_method == old_method)
        new_request.setHTTPBody(request.httpBody());
    return new_request;
}

void WebURLLoaderImpl::loadSynchronously(const WebURLRequest& request,
    WebURLResponse& response,
    WebURLError& error,
    WebData& data,
    int64_t& encoded_data_length,
    int64_t& encoded_body_length)
{
    TRACE_EVENT0("loading", "WebURLLoaderImpl::loadSynchronously");
    SyncLoadResponse sync_load_response;
    context_->Start(request, &sync_load_response);

    const GURL& final_url = sync_load_response.url;

    // TODO(tc): For file loads, we may want to include a more descriptive
    // status code or status text.
    int error_code = sync_load_response.error_code;
    if (error_code != net::OK) {
        response.setURL(final_url);
        error.domain = WebString::fromASCII(net::kErrorDomain);
        error.reason = error_code;
        error.unreachableURL = final_url;
        return;
    }

    PopulateURLResponse(final_url, sync_load_response, &response,
        request.reportRawHeaders());
    response.addToDecodedBodyLength(sync_load_response.data.size());
    encoded_data_length = sync_load_response.encoded_data_length;
    encoded_body_length = sync_load_response.encoded_body_length;

    data.assign(sync_load_response.data.data(), sync_load_response.data.size());
}

void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest& request,
    WebURLLoaderClient* client)
{
    TRACE_EVENT_WITH_FLOW0("loading", "WebURLLoaderImpl::loadAsynchronously",
        this, TRACE_EVENT_FLAG_FLOW_OUT);
    DCHECK(!context_->client());

    context_->set_client(client);
    context_->Start(request, NULL);
}

void WebURLLoaderImpl::cancel()
{
    context_->Cancel();
}

void WebURLLoaderImpl::setDefersLoading(bool value)
{
    context_->SetDefersLoading(value);
}

void WebURLLoaderImpl::didChangePriority(WebURLRequest::Priority new_priority,
    int intra_priority_value)
{
    context_->DidChangePriority(new_priority, intra_priority_value);
}

void WebURLLoaderImpl::setLoadingTaskRunner(
    base::SingleThreadTaskRunner* loading_task_runner)
{
    context_->SetTaskRunner(loading_task_runner);
}

} // namespace content
